package org.oproject.rmi.hessian.core.servlet;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.logging.Logger;

import javax.servlet.GenericServlet;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.oproject.rmi.hessian.core.context.HessianContext;
import org.oproject.rmi.hessian.core.context.InitHessian;
import org.oproject.rmi.hessian.core.invoke.Invokor;
import org.oproject.rmi.hessian.core.invoke.impl.LocalInvokor;
import org.oproject.rmi.hessian.core.service.loader.ServiceLoader;

import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.SerializerFactory;
import com.caucho.hessian.server.HessianServlet;
import com.caucho.hessian.server.HessianSkeleton;
import com.caucho.services.server.Service;
import com.caucho.services.server.ServiceContext;

public class DefaultHessianServlet extends GenericServlet
{
	private static final long serialVersionUID = 1L;

	public DefaultHessianServlet()
	{
	}

	@SuppressWarnings("unused")
	private Logger _log = Logger.getLogger(DefaultHessianServlet.class.getName());

	private Class<Invokor> _homeAPI;

	private Object _homeImpl;

	private HessianSkeleton _homeSkeleton;

	private SerializerFactory _serializerFactory;

	@SuppressWarnings("unused")
	private boolean _isDebug;

	public String getServletInfo()
	{
		return "Hessian Servlet";
	}

	/**
	 * Sets the home api.
	 */
	public void setHomeAPI(Class<Invokor> api)
	{
		_homeAPI = api;
	}

	/**
	 * Sets the home implementation
	 */
	public void setHome(Object home)
	{
		_homeImpl = home;
	}

	/**
	 * Sets the serializer factory.
	 */
	public void setSerializerFactory(SerializerFactory factory)
	{
		_serializerFactory = factory;
	}

	/**
	 * Gets the serializer factory.
	 */
	public SerializerFactory getSerializerFactory()
	{
		if (_serializerFactory == null)
			_serializerFactory = new SerializerFactory();

		return _serializerFactory;
	}

	/**
	 * Sets the serializer send collection java type.
	 */
	public void setSendCollectionType(boolean sendType)
	{
		getSerializerFactory().setSendCollectionType(sendType);
	}

	/**
	 * Sets the debugging flag.
	 */
	public void setDebug(boolean isDebug)
	{
		_isDebug = isDebug;
	}

	/**
	 * Sets the debugging log name.
	 */
	public void setLogName(String name)
	{
		_log = Logger.getLogger(name);
	}

	/**
	 * Initialize the service, including the service object.
	 */
	public void init(ServletConfig config) throws ServletException
	{
		super.init(config);

		try
		{
			_homeImpl = new LocalInvokor();
			init(_homeImpl);

			_homeAPI = Invokor.class;

			_homeSkeleton = new HessianSkeleton(_homeImpl, _homeAPI);

			if ("true".equals(getInitParameter("debug")))
				_isDebug = true;

			if ("false".equals(getInitParameter("send-collection-type")))
				setSendCollectionType(false);

			initHessian();
			
			loadServices();
		}
		catch (ServletException e)
		{
			throw e;
		}
		catch (Exception e)
		{
			throw new ServletException(e);
		}
	}

	/**
	 * 初始化hessian环境
	 */
	@SuppressWarnings("unchecked")
	private void initHessian()
	{
		String initClassName = "org.oproject.rmi.hessian.core.context.DefaultInitHessian";

		if (getInitParameter("init-class") != null)
		{
			initClassName = getInitParameter("init-class");
		}

		if(null != initClassName
				&& !initClassName.trim().equals(""))
		{
			try
			{
				Class<InitHessian> initClass = loadClass(initClassName);
				InitHessian initor = initClass.newInstance();
				initor.init();
			}
			catch (ClassNotFoundException e)
			{
				e.printStackTrace();
			}
			catch (InstantiationException e)
			{
				e.printStackTrace();
			}
			catch (IllegalAccessException e)
			{
				e.printStackTrace();
			}
		}
	}

	/**
	 * 加载服务接口
	 */
	@SuppressWarnings("unchecked")
	private void loadServices()
	{
		//默认为空，表示不需要service 加载器，通过在程序内部手动加载
		String serviceLoadorClassName = null;

		if (getInitParameter("service-loader") != null)
		{
			serviceLoadorClassName = getInitParameter("service-loader");
		}

		if(null != serviceLoadorClassName
				&& !serviceLoadorClassName.trim().equals(""))
		{
			try
			{
				Class<ServiceLoader> serviceLoaderClass = loadClass(serviceLoadorClassName);
				ServiceLoader serviceLoader = serviceLoaderClass.newInstance();
				serviceLoader.regiestService();
			}
			catch (ClassNotFoundException e)
			{
				e.printStackTrace();
			}
			catch (InstantiationException e)
			{
				e.printStackTrace();
			}
			catch (IllegalAccessException e)
			{
				e.printStackTrace();
			}
		}
	}

	@SuppressWarnings("unchecked")
	private Class loadClass(String className) throws ClassNotFoundException
	{
		ClassLoader loader = getContextClassLoader();

		if (loader != null)
			return Class.forName(className, false, loader);
		else
			return Class.forName(className);
	}

	private ClassLoader getContextClassLoader()
	{
		return Thread.currentThread().getContextClassLoader();
	}

	private void init(Object service) throws ServletException
	{
		if (!this.getClass().equals(HessianServlet.class))
		{
		}
		else if (service instanceof Service)
			((Service) service).init(getServletConfig());
		else if (service instanceof Servlet)
			((Servlet) service).init(getServletConfig());
	}

	/**
	 * Execute a request. The path-info of the request selects the bean. Once
	 * the bean's selected, it will be applied.
	 */
	@SuppressWarnings("deprecation")
	public void service(ServletRequest request, ServletResponse response)
			throws IOException, ServletException
	{	
		HttpServletRequest req = (HttpServletRequest) request;
		HttpServletResponse res = (HttpServletResponse) response;
		
		if (!req.getMethod().equals("POST"))
		{
			res.setStatus(500, "Hessian Requires POST");
			PrintWriter out = res.getWriter();

			res.setContentType("text/html");
			out.println("<h1>Hessian Requires POST</h1>");

			return;
		}

		String serviceId = req.getPathInfo();
		String objectId = req.getParameter("id");
		
		if (objectId == null)
			objectId = req.getParameter("ejbid");
		
		ServiceContext.begin(req, serviceId, objectId);

		try
		{
			InputStream is = request.getInputStream();
			OutputStream os = response.getOutputStream();

			response.setContentType("application/x-hessian");

			SerializerFactory serializerFactory = getSerializerFactory();

			invoke(is, os, objectId, serializerFactory);
		}
		catch (RuntimeException e)
		{
			throw e;
		}
		catch (ServletException e)
		{
			throw e;
		}
		catch (Throwable e)
		{
			throw new ServletException(e);
		}
		finally
		{
			ServiceContext.end();
			
			/**
			 * 清除环境变量
			 */
			HessianContext.clear();
		}
	}

	protected void invoke(InputStream is, OutputStream os, String objectId,
			SerializerFactory serializerFactory) throws Exception
	{
		_homeSkeleton.invoke(is, os, serializerFactory);
	}

	protected Hessian2Input createHessian2Input(InputStream is)
	{
		return new Hessian2Input(is);
	}

	static class LogWriter extends Writer
	{
		private Logger _log;

		private StringBuilder _sb = new StringBuilder();

		LogWriter(Logger log)
		{
			_log = log;
		}

		public void write(char ch)
		{
			if (ch == '\n' && _sb.length() > 0)
			{
				_log.fine(_sb.toString());
				_sb.setLength(0);
			}
			else
				_sb.append((char) ch);
		}

		public void write(char[] buffer, int offset, int length)
		{
			for (int i = 0; i < length; i++)
			{
				char ch = buffer[offset + i];

				if (ch == '\n' && _sb.length() > 0)
				{
					_log.fine(_sb.toString());
					_sb.setLength(0);
				}
				else
					_sb.append((char) ch);
			}
		}

		public void flush()
		{
		}

		public void close()
		{
		}
	}
}
